package com.example.oauth.tracing;

import com.example.oauth.utils.SpringContextUtil;
import feign.Client;
import feign.Request;
import feign.Response;
import feign.Util;
import io.opentracing.Span;
import io.opentracing.Tracer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;

import java.io.IOException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.regex.Pattern;

import static feign.Util.UTF_8;
import static feign.Util.decodeOrDefault;

@Slf4j
public class EnhancedTracingClient implements Client {

    private Tracer tracer;
    private FeignRequestTracingUtil feignRequestTracingUtil;
    private Client delegate;

    public EnhancedTracingClient(Client delegate, Tracer tracer) {
        this.delegate = delegate;
        this.tracer = tracer;
        this.feignRequestTracingUtil = new FeignRequestTracingUtil();
    }

    @Override
    public Response execute(Request request, Request.Options options) throws IOException {
        this.feignRequestTracingUtil.onRequest(request, tracer.activeSpan());
        Response response = delegate.execute(request, options);
        return this.feignRequestTracingUtil.onResponse(response, tracer.activeSpan());
    }

    private static class FeignRequestTracingUtil {

        public void onRequest(Request request, Span span) {

            if(isTraced(request)) {
                String path = getPath(request.url());
                span.setOperationName(path);
                span.setTag("http.path", path);

                String requestStr = "";
                if (request.body() != null) {
                    if (request.body().length <= 1024*1024) {
                        Charset charset = request.charset();
                        if (charset == null && request.headers().containsKey(HttpHeaders.CONTENT_TYPE)) {
                            String contentType = StringUtils.join(request.headers().get(HttpHeaders.CONTENT_TYPE), "");
                            if ( !StringUtils.isEmpty(contentType) && (contentType.startsWith(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
                                    || contentType.startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)) ) {
                                // 默认使用UTF-8编码
                                charset = Charset.defaultCharset();
                            }
                        }
                        String bodyText = charset != null
                                ? new String(request.body(), charset)
                                : null;
                        requestStr = bodyText != null ? bodyText : "Binary data";
                    } else {
                        requestStr = "参数大于1M";
                    }
                }
                OwlTraceLog.log(span,"feign.request", requestStr);
            }
        }

        public Response onResponse(Response response, Span span) {
            if(!isTraced(response.request())) {
                return response;
            }
            try {
                if (response.body() != null && !(response.status() == 204 || response.status() == 205)) {
                    byte[] bodyData = Util.toByteArray(response.body().asInputStream());
                    int bodyLength = bodyData.length;
                    if (bodyLength > 0) {
                        String responseStr = decodeOrDefault(bodyData, UTF_8, "Binary data");
                        OwlTraceLog.log(span,"feign.response", StringUtils.defaultString(responseStr, ""));
                    }
                    return response.toBuilder().body(bodyData).build();
                }
            } catch (IOException e) {
                //TODO
            }
            return response;
        }

        private boolean isTraced(Request request) {
            try {
                Pattern skipPattern = (Pattern) SpringContextUtil.getBean("skipPattern");
                if (skipPattern != null) {
                    URL url = new URL(request.url());
                    return !skipPattern.matcher(url.getPath()).matches();
                }
            } catch (Exception e) {
                log.error("",e);
                return true;
            }
            return true;
        }

        private String getPath(String spec) {
            try {
                URL url = new URL(spec);
                return url.getPath();
            } catch (Exception e) {
                // 原样返回
                return spec;
            }
        }

    }

}
